package com.fs.service;

import com.fs.entity.Cleaner;
import com.fs.entity.Door;
import com.fs.entity.Room;
import com.fs.entity.Shop;
import com.fs.entity.Switch;
import com.fs.global.CustomException;
import com.fs.repository.*;
import com.fs.service.bo.BoPageInfo;
import com.fs.service.bo.room.BoCleanerCreateParam;
import com.fs.service.bo.room.BoRoom;
import com.fs.service.bo.room.BoRoomQueryParam;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Transactional
@Validated
public class RoomService {

    private final RoomRepository roomDao;
    private final ShopRepository shopDao;
    private final PriceRepository priceDao;
    private final CleanerRepository cleanerDao;
    private final PurchaseRepository purchaseDao;

    public RoomService(RoomRepository repository, ShopRepository shopDao, CleanerRepository cleanerDao, PriceRepository priceDao,
                       PurchaseRepository purchaseDao) {
        this.roomDao = repository;
        this.shopDao = shopDao;
        this.cleanerDao = cleanerDao;
        this.priceDao = priceDao;
        this.purchaseDao = purchaseDao;
    }

    public void addCleaner(@Valid BoCleanerCreateParam param) {
        Optional<Shop> optionalShop = shopDao.findById(param.getShopId());
        if (!optionalShop.isPresent()) {
            throw new CustomException("未找到此商铺的信息");
        }

        Cleaner cleaner = new Cleaner();
        cleaner.setShop(optionalShop.get());
        cleaner.setName(param.getName());
        cleaner.setPhone(param.getPhone());
        cleanerDao.save(cleaner);
    }

    public BoRoom createRoom(String roomName, Integer shopID) {
        if (ObjectUtils.isEmpty(roomName) || shopID == null || shopID < 1) {
            throw new BadParamException("房间名称为空或门店ID小于1");
        }

        Optional<Shop> optShop = shopDao.findById(shopID);
        if (!optShop.isPresent()) {
            throw new NotFoundException("没有找到门店信息");
        }

        Room room = new Room();
        room.setName(roomName);
        room.setShop(optShop.get());
        roomDao.save(room);
        return new BoRoom(room);
    }

    public BoRoom getByID(int roomID) {
        Optional<Room> optRoom = roomDao.findById(roomID);
        if (!optRoom.isPresent()) {
            throw new NotFoundException("没有找到房间");
        }
        return new BoRoom(optRoom.get());
    }

    public void deleteRoom(int roomID) {
        Optional<Room> optRoom = roomDao.findById(roomID);
        if (!optRoom.isPresent()) {
            throw new NotFoundException("没有找到房间");
        }

        Room room = optRoom.get();
        Integer purchaseCount = purchaseDao.countByRoom(room);
        Integer priceCount = priceDao.countByRoom(room);
        if (purchaseCount > 0 || priceCount > 0) {
            throw new CanNotDeleteException("价格或订单存在关联,不能删除");
        }
        roomDao.delete(room);
    }

    /***
     * 动态查询房间
     * @param param 查询参数,可以按照name或者shop_id查询
     * @param pageNo 为0则查询全部
     * @param pageSize 为0则查询全部
     * @return 分页信息
     */
    public BoPageInfo<BoRoom> query(@NotNull BoRoomQueryParam param, int pageNo, int pageSize) {
        Specification<Room> spec = (Root<Room> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (StringUtils.hasLength(param.getName())) {
                predicates.add(cb.like(root.get("name"), param.getName() + "%"));
            }
            if (param.getShopId() != null) {
                predicates.add(cb.equal(root.get("shop_id"), param.getShopId()));
            }
            if (param.getRoomId() != null) {
                predicates.add(cb.equal(root.get("id"), param.getRoomId()));
            }
            int size = predicates.size();
            return query.where(predicates.toArray(new Predicate[size])).getRestriction();
        };

        if (pageNo > 0 && pageSize > 0) {
            Pageable pageable = PageRequest.of(--pageNo, pageSize, Sort.by(Sort.Direction.DESC, "id"));
            Page<Room> page = roomDao.findAll(spec, pageable);
            List<BoRoom> list = page.getContent().stream().map(BoRoom::new).collect(Collectors.toList());
            return new BoPageInfo<>(list, page.getTotalElements());
        } else {
            List<Room> rooms = roomDao.findAll(spec);
            List<BoRoom> list = rooms.stream().map(BoRoom::new).collect(Collectors.toList());
            return new BoPageInfo<>(list, (long) list.size());
        }

    }

    @Cacheable(cacheNames = "RoomService#findDoorIds")
    public List<Integer> findDoorIds(int roomId) {
        Optional<Room> optRoom = roomDao.findById(roomId);
        if (!optRoom.isPresent()) {
            throw new NotFoundException("没有找到房间");
        }
        List<Door> doors = optRoom.get().getDoors();
        return doors.stream().map(Door::getId).collect(Collectors.toList());
    }

    @Cacheable(cacheNames = "RoomService#findSwitchIds")
    public List<Integer> findSwitchIds(int roomId) {
        Optional<Room> optRoom = roomDao.findById(roomId);
        if (!optRoom.isPresent()) {
            throw new NotFoundException("没有找到房间");
        }
        List<Switch> switches = optRoom.get().getSwitches();
        return switches.stream().map(Switch::getId).collect(Collectors.toList());
    }

    public List<Door> findDoorsOfRoom(int roomId){
        Optional<Room> op = roomDao.findById(roomId);
        if (op.isPresent()) {
            return op.get().getDoors();
        }
        return new ArrayList<>();
    }

    public List<Switch> findSwitchesOfRoom(int switchId) {
        Room room = roomDao.getOne(switchId);
        return room.getSwitches();
    }
}
